/*
	MIDAS Context Based Routing
	cbr_reqresp.cc
	Copyright (c) 2007 Radoslaw Olgierd Schoeneich
	Copyright (c) 2007 Bartlomiej Zolnierkiewicz
	MEAG IT PW
*/

#include <algorithm>

#include "scheduler.h"
#include "packet.h"

#include "cbr_packet.h"
#include "serializer.h"
#include "cbr_debug.h"
#include "cbr_address.h"
#include "cbr_msgcache.h"

/**
 * RREQ1/RREQ2 packet format:
 *
 * struct hdr_cbr cmn_hdr	common header
 * u_int32_t transaction_ID	transaction (message) ID
 * u_int32_t total_length	length of entire message
 * <stringz> address		context address
 * u_int8_t data[]		payload
 *
 * Note: RREQ1 and RREQ2 use the same packet format.
 */

/**
 * RREQ1/RREQ2 packet constructor.
 *
 * @param pkt generic packet
 *
 * Used on reception of RREQ1/RREQ2 packet.
 */

cbr_RReqPacket::cbr_RReqPacket(Packet *pkt)
{
	hdr_cbr* generic_hdr = hdr_cbr::access(pkt);
	std::string strAddr;

	this->pkt_ = pkt;
	this->src = (cbr_ID) generic_hdr->src;
	this->type = generic_hdr->type & CBRTYPE_MASK;
	this->size = generic_hdr->size;
	this->mcast = ((generic_hdr->type & CBRTYPE_MCAST) != 0);

	PacketData *data = (PacketData *)pkt->userdata();
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, this->size - sizeof(hdr_cbr));

	ser >> this->transactionID >> this->totalLength;
	ser >> strAddr;
	this->addr = new cbr_ContextAddress(strAddr);
	this->payloadLength = this->size - ser.get_pos();
	this->payload = new char[this->payloadLength];
	std::copy(start + ser.get_pos(),
		  start + ser.get_pos() + this->payloadLength,
		  this->payload);
	cbr_log(" payload: %s\n", this->payload);
}

/**
 * RREQ1/RREQ2 packet constructor.
 *
 * @param src source node ID
 * @param msg message
 * @param mcast multicast flag
 *
 * Used for generating new RREQ1/RREQ2 packet.
 */

cbr_RReqPacket::cbr_RReqPacket(cbr_ID src, cbr_Msg* msg, bool mcast)
{
	this->pkt_ = Packet::alloc();
	this->src = src;
	this->type = CBRTYPE_RREQ1;
	this->mcast = mcast;

	this->addr = new cbr_ContextAddress(*(msg->getAddress()));
	std::string strAddr = this->addr->getAsString();

	this->transactionID = msg->getTransactionID();
	// FIXME: do we really need this? it is always == 0
	this->totalLength = msg->getTotalLength();

	int32_t len = cbr_Packet::maxSize - sizeof(hdr_cbr)
			- 8 - (strAddr.length() + 1);

	this->payload = new char[len];
	this->payloadLength = msg->getPayload(this->payload, 0, len);

	hdr_cbr* generic_hdr = hdr_cbr::access(this->pkt_);
	if (msg->getTotalLength() <= len) {
		// entire payload fits in this packet
		this->type = CBRTYPE_RREQ2;
		generic_hdr->type = CBRTYPE_RREQ2;
	} else {
		generic_hdr->type = CBRTYPE_RREQ1;
	}
	if (this->mcast)
		generic_hdr->type |= CBRTYPE_MCAST;
	generic_hdr->src = (u_int32_t) src;

	PacketData *data = new PacketData(cbr_Packet::maxSize - sizeof(hdr_cbr));
	this->pkt_->setdata(data);
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, cbr_Packet::maxSize - sizeof(hdr_cbr));

	ser << this->transactionID << this->totalLength;
	ser << strAddr;
	std::copy(payload, payload + payloadLength, start + ser.get_pos());
	this->size = sizeof(hdr_cbr) + ser.get_pos() + payloadLength;
	generic_hdr->size = this->size;
}

/**
 * RREQ1/RREQ2 packet destructor.
 */

cbr_RReqPacket::~cbr_RReqPacket()
{
	if (this->payload)
		delete[] this->payload;
	if (this->addr)
		delete this->addr;
}

/**
 * RREP packet format:
 *
 * struct hdr_cbr cmn_hdr	common header
 * u_int32_t dest		destination node ID
 * u_int32_t transaction_ID	transaction (message) ID
 */

/**
 * RREP packet constructor.
 *
 * @param pkt generic packet
 *
 * Used on reception of RREP packet.
 */

cbr_RRepPacket::cbr_RRepPacket(Packet *pkt)
{
	hdr_cbr* generic_hdr = hdr_cbr::access(pkt);

	this->pkt_ = pkt;
	this->src = (cbr_ID) generic_hdr->src;
	this->type = generic_hdr->type & CBRTYPE_MASK;
	this->size = generic_hdr->size;

	PacketData *data = (PacketData *)pkt->userdata();
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, this->size - sizeof(hdr_cbr));

	ser >> this->dest >> this->transactionID;
}

/**
 * RREP packet constructor.
 *
 * @param src source node ID
 * @param dest destination node ID
 * @param trID transaction (message) ID
 *
 * Used for generating new RREP packet.
 */

cbr_RRepPacket::cbr_RRepPacket(cbr_ID src, cbr_ID dest, u_int32_t trID)
{
	this->pkt_ = Packet::alloc();
	this->src = src;
	this->dest = dest;
	this->type = CBRTYPE_RREP;
	this->transactionID = trID;

	hdr_cbr* generic_hdr = hdr_cbr::access(this->pkt_);
	generic_hdr->type = CBRTYPE_RREP;
	generic_hdr->src = (u_int32_t) src;

	PacketData *data = new PacketData(cbr_Packet::maxSize - sizeof(hdr_cbr));
	this->pkt_->setdata(data);
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, cbr_Packet::maxSize - sizeof(hdr_cbr));

	ser << this->dest << this->transactionID;
	this->size = generic_hdr->size = sizeof(hdr_cbr) + ser.get_pos();
}
